package search

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"

	"searchcenter/internal/config"
	"searchcenter/internal/middleware"
	"searchcenter/internal/svc"
	"searchcenter/internal/types"

	"github.com/elastic/go-elasticsearch/v8/esapi"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/rest/httpc"
)

type SearchBlogLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewSearchBlogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SearchBlogLogic {
	return &SearchBlogLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *SearchBlogLogic) SearchBlog(req *types.SearchRequest) (resp *types.SearchResponse, err error) {
	// 获取 Authorization 头
    authToken, _ := l.ctx.Value(middleware.TokenKey).(string)
    logx.Infof("用户Token: %s", authToken)

	// 初始化响应
	resp = &types.SearchResponse{
		BaseResponse: types.BaseResponse{
			Code:    200,
			Message: "success",
		},
		Data:  make([]types.Blog, 0),
		Total: 0,
	}

	// 构建 Elasticsearch 查询
	query := l.buildSearchQuery(req)

	// 转换查询为JSON
	queryJSON, err := json.Marshal(query)
	if err != nil {
		l.Logger.Errorf("Failed to marshal search query: %v", err)
		resp.Code = 500
		resp.Message = "internal server error"
		return resp, err
	}

	// 执行搜索 - 使用正确的 Search 方法调用方式
	searchResult, err := l.svcCtx.ESClient.Search(
		// l.ctx, // 使用请求上下文
		func(o *esapi.SearchRequest) {
			o.Index = []string{"blog"}
			o.Body = strings.NewReader(string(queryJSON))
			o.From = esapi.IntPtr((req.Page - 1) * req.PageSize)
			o.Size = esapi.IntPtr(req.PageSize)
		},
	)
	if err != nil {
		l.Logger.Errorf("ES search failed: %v", err)
		resp.Code = 500
		resp.Message = "search service unavailable"
		return resp, err
	}
	defer searchResult.Body.Close()

	// 检查响应错误
	if searchResult.IsError() {
		errMsg, _ := io.ReadAll(searchResult.Body)
		l.Logger.Errorf("ES search error: %s", string(errMsg))
		resp.Code = 500
		resp.Message = "search service error"
		return resp, fmt.Errorf("%s", string(errMsg))
	}

	// 解析搜索结果
	var result map[string]interface{}
	if err := json.NewDecoder(searchResult.Body).Decode(&result); err != nil {
		l.Logger.Errorf("Failed to decode search result: %v", err)
		resp.Code = 500
		resp.Message = "failed to parse search result"
		return resp, err
	}

	// 处理搜索结果
	if err := l.parseSearchResult(result, resp, authToken); err != nil {
		l.Logger.Errorf("Parse search result failed: %v", err)
		resp.Code = 500
		resp.Message = "parse search result failed"
		return resp, err
	}

	return resp, nil
}

// buildSearchQuery 构建 Elasticsearch 查询
func (l *SearchBlogLogic) buildSearchQuery(req *types.SearchRequest) map[string]interface{} {
	// 基础 bool 查询
	boolQuery := map[string]interface{}{
		"must":   make([]interface{}, 0),
		"filter": make([]interface{}, 0),
	}

	// 支持模糊搜索
	if req.Keyword != "" {
		boolQuery["must"] = append(boolQuery["must"].([]interface{}), map[string]interface{}{
			"bool": map[string]interface{}{
				"should": []interface{}{ // 多种匹配方式组合
					// 精确匹配（高权重）
					map[string]interface{}{
						"match": map[string]interface{}{
							"title": map[string]interface{}{
								"query": req.Keyword,
								"boost": 3,
							},
						},
					},
					// 模糊匹配
					map[string]interface{}{
						"fuzzy": map[string]interface{}{
							"title": map[string]interface{}{
								"value":     req.Keyword,
								"fuzziness": 2, // 允许最多2个字符差异
							},
						},
					},
					// 内容模糊匹配
					map[string]interface{}{
						"match": map[string]interface{}{
							"content": map[string]interface{}{
								"query":     req.Keyword,
								"fuzziness": "AUTO",
							},
						},
					},
					// 标签模糊搜索
					map[string]interface{}{
						"match": map[string]interface{}{
							"tags": map[string]interface{}{
								"query":     req.Keyword,
								"fuzziness": 1, // 标签通常较短，设置较小的模糊度
							},
						},
					},
					// 分类模糊搜索
					map[string]interface{}{
						"match": map[string]interface{}{
							"category": map[string]interface{}{
								"query":     req.Keyword,
								"fuzziness": 1, // 标签通常较短，设置较小的模糊度
							},
						},
					},
				},
				"minimum_should_match": 1, // 至少满足一个条件
			},
		})
	}

	// 作者筛选
	if req.AuthorId != 0 {
		boolQuery["filter"] = append(boolQuery["filter"].([]interface{}), map[string]interface{}{
			"term": map[string]interface{}{
				"AuthorId": req.AuthorId,
			},
		})
	}

	// 标签筛选
	if len(req.Tags) > 0 {
		boolQuery["filter"] = append(boolQuery["filter"].([]interface{}), map[string]interface{}{
			"terms": map[string]interface{}{
				"tags": req.Tags,
			},
		})
	}

	// 时间范围筛选
	if req.StartTime != "" || req.EndTime != "" {
		timeRange := make(map[string]interface{})
		if req.StartTime != "" {
			timeRange["gte"] = req.StartTime
		}
		if req.EndTime != "" {
			timeRange["lte"] = req.EndTime
		}

		boolQuery["filter"] = append(boolQuery["filter"].([]interface{}), map[string]interface{}{
			"range": map[string]interface{}{
				"createTime": timeRange,
			},
		})
	}

	// 构建完整查询
	query := map[string]interface{}{
		"query": map[string]interface{}{
			"bool": boolQuery,
		},
	}

	// 添加排序
	if req.SortField != "" {
		sortOrder := "asc"
		if req.SortOrder == "desc" {
			sortOrder = "desc"
		}

		query["sort"] = []map[string]interface{}{
			{
				req.SortField: map[string]interface{}{
					"order": sortOrder,
				},
			},
		}
	}

	return query
}

// parseSearchResult 解析 Elasticsearch 返回结果
func (l *SearchBlogLogic) parseSearchResult(searchResult map[string]interface{}, resp *types.SearchResponse, authToken string) error {
	// 获取匹配总数
	if total, ok := searchResult["hits"].(map[string]interface{})["total"].(map[string]interface{})["value"].(float64); ok {
		resp.Total = int64(total)
	}

	// 解析搜索结果
	hits, ok := searchResult["hits"].(map[string]interface{})["hits"].([]interface{})
	if !ok {
		return nil
	}

	for _, hit := range hits {
		hitMap := hit.(map[string]interface{})
		source := hitMap["_source"].(map[string]interface{})

		var blog types.Blog
		blogInf, err := l.getBlogInfo(int(source["id"].(float64)), authToken)
		if err != nil {
			blog = types.Blog{
				Id:         int(source["id"].(float64)),
				UserId:     int(source["AuthorId"].(float64)),
				Title:      source["title"].(string),
				Content:    source["content"].(string),
				LikeNum:    int(source["likeNum"].(float64)),
				JoyNum:     int(source["joyNum"].(float64)),
				JoyFeeling: source["joyFeeling"].(string),
				Place:      source["place"].(string),
				CommentNum: int(source["commentNum"].(float64)),
				CreateTime: source["createTime"].(string),
				ReadNum:    int(source["readNum"].(float64)),
				Category:   source["category"].(string),
			}
			logx.Error(fmt.Errorf("获取 blog 信息错误：%v", err))
		} else {
			blog = types.Blog{
				Id:         int(source["id"].(float64)),
				UserId:     int(source["AuthorId"].(float64)),
				Title:      source["title"].(string),
				Content:    source["content"].(string),
				LikeNum:    int(source["likeNum"].(float64)),
				JoyNum:     int(source["joyNum"].(float64)),
				JoyFeeling: source["joyFeeling"].(string),
				Place:      source["place"].(string),
				CommentNum: int(source["commentNum"].(float64)),
				CreateTime: source["createTime"].(string),
				ReadNum:    blogInf.Id,
				Category:   source["category"].(string),
				Photo:      blogInf.Photo,
			}
		}

		// 处理 tags
		if tags, ok := source["tags"].([]interface{}); ok {
			blog.Tags = make([]string, len(tags))
			for i, tag := range tags {
				blog.Tags[i] = tag.(string)
			}
		}

		resp.Data = append(resp.Data, blog)
	}

	return nil
}

func (l *SearchBlogLogic) getBlogInfo(blogID int, authToken string) (*types.Blog, error) {
	type BlogInfoRespon struct {
		Code    int        `json:"code"`
		Message string     `json:"message"`
		Data    types.Blog `json:"data"`
	}
	// 确保context不为nil
	if l.ctx == nil {
		l.ctx = context.Background()
		logx.WithContext(l.ctx).Errorf("context为nil，使用background context")
	}
	cfg := config.GetBaseConfig()
	consulURL := fmt.Sprintf("http://%s:%s", cfg.Consul.Host, "/v1/health/service/blog-api?passing")
	// 2. 从Consul获取blog-api服务地址
	consulResp, err := httpc.Do(l.ctx, "GET", consulURL, nil)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("consul请求失败, error: %v", err)
		return nil, fmt.Errorf("获取服务地址失败: %v", err)
	}
	defer consulResp.Body.Close()

	if consulResp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(consulResp.Body)
		logx.WithContext(l.ctx).Errorf("consul返回非200状态码, status: %d, body: %s",
			consulResp.StatusCode, string(body))
		return nil, fmt.Errorf("consul返回错误状态码: %d", consulResp.StatusCode)
	}

	var services []struct {
		Service struct {
			Address string `json:"Address"`
			Port    int    `json:"Port"`
		} `json:"Service"`
	}

	consulBody, err := ioutil.ReadAll(consulResp.Body)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("读取consul响应体失败, error: %v", err)
		return nil, fmt.Errorf("读取服务地址失败: %v", err)
	}

	if err := json.Unmarshal(consulBody, &services); err != nil {
		logx.WithContext(l.ctx).Errorf("解析consul响应失败, error: %v, body: %s",
			err, string(consulBody))
		return nil, fmt.Errorf("解析服务地址失败: %v", err)
	}

	if len(services) == 0 {
		logx.WithContext(l.ctx).Errorf("无可用blog-api服务实例")
		return nil, fmt.Errorf("无可用服务实例")
	}

	// 随机选择一个实例
	rand.Seed(time.Now().UnixNano())
	instance := services[rand.Intn(len(services))]
	baseURL := fmt.Sprintf("http://%s:%d", instance.Service.Address, instance.Service.Port)

	// 3. 构建请求URL
	// 4. 发送HTTP请求到用户服务
	// resp, err := httpc.Do(l.ctx, "GET", baseURL+"/api/blog/blog/"+strconv.Itoa(blogID), nil)
	// 创建带有Authorization头的请求
	url := baseURL + "/api/blog/blog/" + strconv.Itoa(blogID)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("创建请求失败: %v", err)
	}

	req.Header.Set("Authorization", "Bearer "+authToken)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("获取用户信息请求失败, blogID: %d, error: %v", blogID, err)
		return nil, fmt.Errorf("获取用户信息请求失败: %v", err)
	}
	defer resp.Body.Close() // 确保关闭响应体

	// 5. 检查HTTP状态码
	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		logx.WithContext(l.ctx).Errorf("用户服务返回非200状态码, blogID: %d, status: %d, body: %s",
			blogID, resp.StatusCode, string(body))
		return nil, fmt.Errorf("用户服务返回错误状态码: %d", resp.StatusCode)
	}

	// 6. 读取响应体
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("读取响应体失败, blogID: %d, error: %v", blogID, err)
		return nil, fmt.Errorf("读取响应体失败: %v", err)
	}

	// 7. 解析JSON
	var result BlogInfoRespon
	if err := json.Unmarshal(body, &result); err != nil {
		logx.WithContext(l.ctx).Errorf("解析用户信息响应失败, blogID: %d, error: %v, body: %s",
			blogID, err, string(body))
		return nil, fmt.Errorf("解析用户信息响应失败: %v", err)
	}

	// 8. 检查业务状态码
	if result.Code != 200 {
		logx.WithContext(l.ctx).Errorf("用户服务返回业务错误, blogID: %d, code: %d, message: %s",
			blogID, result.Code, result.Message)
		return nil, fmt.Errorf("用户服务错误: %s", result.Message)
	}

	// 9. 返回用户信息
	return &types.Blog{
		Id:         result.Data.Id,
		Title:      result.Data.Title,
		Content:    result.Data.Content,
		Photo:      result.Data.Photo,
		LikeNum:    result.Data.LikeNum,
		ReadNum:    result.Data.ReadNum,
		JoyNum:     result.Data.JoyNum,
		JoyFeeling: result.Data.JoyFeeling,
		Place:      result.Data.Place,
		CommentNum: result.Data.CommentNum,
		Category:   result.Data.Category,
	}, nil
}
